package com.itcam.otcbackend.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itcam.otcbackend.enums.ErrorCode;
import com.itcam.otcbackend.exception.BusinessException;
import com.itcam.otcbackend.mapper.SpaceMapper;
import com.itcam.otcbackend.model.dto.space.analyze.request.*;
import com.itcam.otcbackend.model.dto.space.analyze.response.*;
import com.itcam.otcbackend.model.po.Picture;
import com.itcam.otcbackend.model.po.Space;
import com.itcam.otcbackend.model.po.User;
import com.itcam.otcbackend.model.dto.space.analyze.SpaceAnalyzeRequest;
import com.itcam.otcbackend.service.PictureService;
import com.itcam.otcbackend.service.SpaceAnalyzeService;
import com.itcam.otcbackend.service.SpaceService;
import com.itcam.otcbackend.service.UserService;
import com.itcam.otcbackend.utils.ThrowUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class SpaceAnalyzeServiceImpl extends ServiceImpl<SpaceMapper, Space>
        implements SpaceAnalyzeService {

    @Resource
    private UserService userService;

    @Resource
    private SpaceService spaceService;

    @Resource
    private PictureService pictureService;

    /**
     * todo:获取空间使用分析数据
     *
     * @param spaceUsageAnalyzeRequest SpaceUsageAnalyzeRequest 请求参数，包含查询范围、是否查询全部或公共图库等信息
     * @param loginUser                当前登录用户，用于权限验证
     * @return SpaceUsageAnalyzeResponse 分析结果，包含空间使用大小、数量、最大限制及使用比例等信息
     */
    @Override
    public SpaceUsageAnalyzeResponse getSpaceUsageAnalyze(SpaceUsageAnalyzeRequest spaceUsageAnalyzeRequest, User loginUser) {
        // 检查请求参数是否为空，如果为空则抛出参数错误异常
        ThrowUtils.throwIf(spaceUsageAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);
        if (spaceUsageAnalyzeRequest.isQueryAll() || spaceUsageAnalyzeRequest.isQueryPublic()) {
            // 查询全部或公共图库逻辑
            // 仅管理员可以访问
            // 调用 userService 的 isAdmin 方法判断当前用户是否为管理员
            boolean isAdmin = userService.isAdmin(loginUser);
            // 如果不是管理员，抛出无权限访问空间的业务异常
            ThrowUtils.throwIf(!isAdmin, ErrorCode.NO_AUTH_ERROR, "无权访问空间");
            // 统计公共图库的资源使用
            // 创建一个 MyBatis-Plus 的查询包装器，用于构建 SQL 查询条件
            QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
            // 指定查询 picSize 字段
            queryWrapper.select("picSize");
            // 如果不是查询全部，添加查询条件：spaceId 字段为空
            if (!spaceUsageAnalyzeRequest.isQueryAll()) {
                queryWrapper.isNull("spaceId");
            }
            // 执行查询，获取图片大小的对象列表
            List<Object> pictureObjList = pictureService.getBaseMapper().selectObjs(queryWrapper);
            // 计算使用的总大小
            long usedSize = pictureObjList.stream().mapToLong(result -> result instanceof Long ? (Long) result : 0).sum();
            // 计算使用的图片数量
            long usedCount = pictureObjList.size();
            // 封装返回结果
            SpaceUsageAnalyzeResponse spaceUsageAnalyzeResponse = new SpaceUsageAnalyzeResponse();
            // 设置使用的总大小
            spaceUsageAnalyzeResponse.setUsedSize(usedSize);
            // 设置使用的图片数量
            spaceUsageAnalyzeResponse.setUsedCount(usedCount);
            // 公共图库无上限、无比例
            // 设置最大大小为 null
            spaceUsageAnalyzeResponse.setMaxSize(null);
            // 设置大小使用比例为 null
            spaceUsageAnalyzeResponse.setSizeUsageRatio(null);
            // 设置最大数量为 null
            spaceUsageAnalyzeResponse.setMaxCount(null);
            // 设置数量使用比例为 null
            spaceUsageAnalyzeResponse.setCountUsageRatio(null);
            return spaceUsageAnalyzeResponse;
        } else {
            // 查询指定空间
            // 从请求参数中获取空间 ID
            Long spaceId = spaceUsageAnalyzeRequest.getSpaceId();
            // 检查空间 ID 是否为空或小于等于 0，如果是则抛出参数错误异常
            ThrowUtils.throwIf(spaceId == null || spaceId <= 0, ErrorCode.PARAMS_ERROR);
            // 获取空间信息
            // 根据空间 ID 从数据库中获取空间信息
            Space space = spaceService.getById(spaceId);
            // 检查空间是否存在，如果不存在则抛出空间不存在的业务异常
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");

            // 权限校验：仅空间所有者或管理员可访问
            // 调用 spaceService 的 checkSpaceAuth 方法，检查当前用户是否有访问该空间的权限
            spaceService.checkSpaceAuth(loginUser, space);

            // 构造返回结果
            SpaceUsageAnalyzeResponse response = new SpaceUsageAnalyzeResponse();
            // 设置使用的总大小
            response.setUsedSize(space.getTotalSize());
            // 设置最大大小
            response.setMaxSize(space.getMaxSize());
            // 后端直接算好百分比，这样前端可以直接展示
            // 计算大小使用比例，保留两位小数
            double sizeUsageRatio = NumberUtil.round(space.getTotalSize() * 100.0 / space.getMaxSize(), 2).doubleValue();
            // 设置大小使用比例
            response.setSizeUsageRatio(sizeUsageRatio);
            // 设置使用的图片数量
            response.setUsedCount(space.getTotalCount());
            // 设置最大数量
            response.setMaxCount(space.getMaxCount());
            // 计算数量使用比例，保留两位小数
            double countUsageRatio = NumberUtil.round(space.getTotalCount() * 100.0 / space.getMaxCount(), 2).doubleValue();
            // 设置数量使用比例
            response.setCountUsageRatio(countUsageRatio);
            return response;
        }
    }


    /**
     * todo:获取空间图片分类分析数据
     *
     * 此方法用于根据传入的空间分类分析请求和登录用户信息，
     * 对空间内的图片按分类进行分析，返回每个分类的图片数量和总大小信息。
     *
     * @param spaceCategoryAnalyzeRequest 空间分类分析请求对象，包含分析的范围等信息
     * @param loginUser 当前登录的用户信息，用于权限验证
     * @return 包含空间图片分类分析结果的列表，每个结果对应一个图片分类
     */
    @Override
    public List<SpaceCategoryAnalyzeResponse> getSpaceCategoryAnalyze(SpaceCategoryAnalyzeRequest spaceCategoryAnalyzeRequest, User loginUser) {
        // 检查请求对象是否为空，如果为空则抛出参数错误异常
        ThrowUtils.throwIf(spaceCategoryAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);

        // 检查权限
        // 调用 checkSpaceAnalyzeAuth 方法，验证用户是否有进行空间分析的权限
        checkSpaceAnalyzeAuth(spaceCategoryAnalyzeRequest, loginUser);

        // 构造查询条件
        // 创建一个 MyBatis-Plus 的查询包装器，用于构建 SQL 查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        // 根据分析范围补充查询条件
        // 调用 fillAnalyzeQueryWrapper 方法，根据请求中的分析范围信息，为查询包装器添加相应的查询条件
        fillAnalyzeQueryWrapper(spaceCategoryAnalyzeRequest, queryWrapper);

        // 使用 MyBatis-Plus 分组查询
        // 指定查询的字段，包括分类名称、该分类下的图片数量和总大小
        queryWrapper.select("category AS category",
                        "COUNT(*) AS count",
                        "SUM(picSize) AS totalSize")
                // 按分类字段进行分组
                .groupBy("category");

        // 查询并转换结果
        // 执行查询，返回一个包含查询结果的 Map 列表
        return pictureService.getBaseMapper().selectMaps(queryWrapper)
                // 将结果列表转换为流
                .stream()
                // 对每个查询结果进行映射转换
                .map(result -> {
                    // 获取分类名称，如果为空则默认为 "未分类"
                    String category = result.get("category") != null ? result.get("category").toString() : "未分类";
                    // 获取该分类下的图片数量
                    Long count = ((Number) result.get("count")).longValue();
                    // 获取该分类下的图片总大小
                    Long totalSize = ((Number) result.get("totalSize")).longValue();
                    // 创建一个 SpaceCategoryAnalyzeResponse 对象，包含分类名称、图片数量和总大小
                    return new SpaceCategoryAnalyzeResponse(category, count, totalSize);
                })
                // 将转换后的对象收集到一个列表中并返回
                .collect(Collectors.toList());
    }

    /**
     * todo:获取空间图片标签分析数据
     * @param spaceTagAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceTagAnalyzeResponse> getSpaceTagAnalyze(SpaceTagAnalyzeRequest spaceTagAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceTagAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);

        // 检查权限
        checkSpaceAnalyzeAuth(spaceTagAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        fillAnalyzeQueryWrapper(spaceTagAnalyzeRequest, queryWrapper);

        // 查询所有符合条件的标签
        queryWrapper.select("tags");
        List<String> tagsJsonList = pictureService.getBaseMapper().selectObjs(queryWrapper)
                .stream()
                .filter(ObjectUtil::isNotEmpty)
                .map(Object::toString)
                .collect(Collectors.toList());

        // 合并所有标签并统计使用次数
        Map<String, Long> tagCountMap = tagsJsonList.stream()
                .flatMap(tagsJson -> JSONUtil.toList(tagsJson, String.class).stream())
                .collect(Collectors.groupingBy(tag -> tag, Collectors.counting()));

        // 转换为响应对象，按使用次数降序排序
        return tagCountMap.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e2.getValue(), e1.getValue())) // 降序排序
                .map(entry -> new SpaceTagAnalyzeResponse(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());

    }

    /**
     * todo:获取空间图片大小分析数据
     * @param spaceSizeAnalyzeRequest 空间大小分析请求对象，包含分析的范围等信息
     * @param loginUser 当前登录的用户信息，用于权限验证
     * @return 包含空间图片大小分析结果的列表，每个结果对应一个图片大小分段
     */
    @Override
    public List<SpaceSizeAnalyzeResponse> getSpaceSizeAnalyze(SpaceSizeAnalyzeRequest spaceSizeAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceSizeAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);

        // 检查权限
        checkSpaceAnalyzeAuth(spaceSizeAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        fillAnalyzeQueryWrapper(spaceSizeAnalyzeRequest, queryWrapper);

        // 查询所有符合条件的图片大小
        queryWrapper.select("picSize");
        List<Long> picSizes = pictureService.getBaseMapper().selectObjs(queryWrapper)
                .stream()
                .map(size -> ((Number) size).longValue())
                .collect(Collectors.toList());

        // 定义分段范围，注意使用有序 Map
        Map<String, Long> sizeRanges = new LinkedHashMap<>();
        sizeRanges.put("<100KB", picSizes.stream().filter(size -> size < 100 * 1024).count());
        sizeRanges.put("100KB-500KB", picSizes.stream().filter(size -> size >= 100 * 1024 && size < 500 * 1024).count());
        // 修复乘法表达式
        sizeRanges.put("500KB-1MB", picSizes.stream().filter(size -> size >= 500 * 1024 && size < 1024 * 1024).count());
        // 修复乘法表达式
        sizeRanges.put(">1MB", picSizes.stream().filter(size -> size >= 1024 * 1024).count());

        // 转换为响应对象
        return sizeRanges.entrySet().stream()
                .map(entry -> new SpaceSizeAnalyzeResponse(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * todo:获取用户上传行为分析数据
     *
     * 该方法根据传入的空间用户分析请求和登录用户信息，
     * 对用户的上传行为进行分析，支持按日、周、月的时间维度统计。
     *
     * @param spaceUserAnalyzeRequest 空间用户分析请求对象，包含用户 ID、时间维度等信息
     * @param loginUser 当前登录的用户信息，用于权限验证
     * @return 包含用户上传行为分析结果的列表，每个结果对应一个时间段和上传数量
     */
    @Override
    public List<SpaceUserAnalyzeResponse> getSpaceUserAnalyze(SpaceUserAnalyzeRequest spaceUserAnalyzeRequest, User loginUser) {
        // 检查请求参数是否为空，如果为空则抛出参数错误异常
        ThrowUtils.throwIf(spaceUserAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);
        // 检查用户是否有进行空间分析的权限
        checkSpaceAnalyzeAuth(spaceUserAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        // 从请求参数中获取用户 ID
        Long userId = spaceUserAnalyzeRequest.getUserId();
        // 如果用户 ID 不为空，添加查询条件：userId 字段等于指定的用户 ID
        queryWrapper.eq(ObjUtil.isNotNull(userId), "userId", userId);
        // 根据分析范围填充查询对象
        fillAnalyzeQueryWrapper(spaceUserAnalyzeRequest, queryWrapper);

        // 分析维度：每日、每周、每月
        String timeDimension = spaceUserAnalyzeRequest.getTimeDimension();
        switch (timeDimension) {
            case "day":
                // 按天统计，将 createTime 格式化为 YYYY-MM-DD 作为时间段
                queryWrapper.select("DATE_FORMAT(createTime, '%Y-%m-%d') AS period", "COUNT(*) AS count");
                break;
            case "week":
                // 按周统计，使用 YEARWEEK 函数获取年份和周数作为时间段
                queryWrapper.select("YEARWEEK(createTime) AS period", "COUNT(*) AS count");
                break;
            case "month":
                // 按月统计，将 createTime 格式化为 YYYY-MM 作为时间段
                queryWrapper.select("DATE_FORMAT(createTime, '%Y-%m') AS period", "COUNT(*) AS count");
                break;
            default:
                // 若时间维度不支持，抛出参数错误异常
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持的时间维度");
        }

        // 按时间段分组，并按时间段升序排序
        queryWrapper.groupBy("period").orderByAsc("period");

        // 执行查询并获取结果
        List<Map<String, Object>> queryResult = pictureService.getBaseMapper().selectMaps(queryWrapper);
        // 将查询结果转换为 SpaceUserAnalyzeResponse 对象列表
        return queryResult.stream()
                .map(result -> {
                    // 获取时间段
                    String period = result.get("period").toString();
                    // 获取上传数量
                    Long count = ((Number) result.get("count")).longValue();
                    return new SpaceUserAnalyzeResponse(period, count);
                })
                .collect(Collectors.toList());
    }


    /**
     * todo:校验空间分析权限
     *
     * @param spaceAnalyzeRequest 空间分析请求对象，包含分析的范围等信息
     * @param loginUser 当前登录的用户信息，用于权限验证
     */
    private void checkSpaceAnalyzeAuth(SpaceAnalyzeRequest spaceAnalyzeRequest, User loginUser) {
        // 检查权限
        // 判断请求是否为全空间分析或者查询公共图库
        if (spaceAnalyzeRequest.isQueryAll() || spaceAnalyzeRequest.isQueryPublic()) {
            // 全空间分析或者公共图库权限校验：仅管理员可访问
            // 调用 userService 的 isAdmin 方法判断当前用户是否为管理员
            // 如果不是管理员，使用 ThrowUtils.throwIf 抛出无权限访问的业务异常
            ThrowUtils.throwIf(!userService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR, "无权访问公共图库");
        } else {
            // 私有空间权限校验
            // 从请求对象中获取要分析的空间 ID
            Long spaceId = spaceAnalyzeRequest.getSpaceId();
            // 检查空间 ID 是否为空或者小于等于 0，如果是则抛出参数错误异常
            ThrowUtils.throwIf(spaceId == null || spaceId <= 0, ErrorCode.PARAMS_ERROR);
            // 根据空间 ID 从数据库中获取空间信息
            Space space = spaceService.getById(spaceId);
            // 检查获取的空间信息是否为空，如果为空则抛出空间不存在的业务异常
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
            // 调用 spaceService 的 checkSpaceAuth 方法，检查当前用户是否有访问该空间的权限
            spaceService.checkSpaceAuth(loginUser, space);
        }
    }


    /**
     * todo:根据分析范围填充查询对象
     *
     * @param spaceAnalyzeRequest 空间分析请求对象，包含查询范围的相关信息，如是否查询全量数据、是否查询公共图库、指定的空间 ID 等
     * @param queryWrapper MyBatis-Plus 的查询包装器对象，用于构建 SQL 查询条件
     */
    private static void fillAnalyzeQueryWrapper(SpaceAnalyzeRequest spaceAnalyzeRequest, QueryWrapper<Picture> queryWrapper) {
        // 如果请求是查询全量数据，不做额外条件添加，直接返回
        if (spaceAnalyzeRequest.isQueryAll()) {
            return;
        }
        // 如果请求是查询公共图库，添加查询条件：spaceId 字段为空
        if (spaceAnalyzeRequest.isQueryPublic()) {
            queryWrapper.isNull("spaceId");
            return;
        }
        // 从请求对象中获取指定的空间 ID
        Long spaceId = spaceAnalyzeRequest.getSpaceId();
        // 如果空间 ID 不为空，添加查询条件：spaceId 字段等于指定的空间 ID
        if (spaceId != null) {
            queryWrapper.eq("spaceId", spaceId);
            return;
        }
        // 如果以上条件都不满足，说明未指定有效的查询范围，抛出参数错误异常
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "未指定查询范围");
    }


}
